-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Initial support for emitting coroutine body #161616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CIR] Initial support for emitting coroutine body #161616
Conversation
@llvm/pr-subscribers-clang Author: None (Andres-Salamanca) ChangesThis PR adds new Full diff: https://github.com/llvm/llvm-project/pull/161616.diff 12 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bb394440bf8d8..c19b90d30bf94 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2326,6 +2326,12 @@ def CIR_FuncOp : CIR_Op<"func", [
The function linkage information is specified by `linkage`, as defined by
`GlobalLinkageKind` attribute.
+ A compiler builtin function must be marked as `builtin` for further
+ processing when lowering from CIR.
+
+ The `coroutine` keyword is used to mark coroutine function, which requires
+ at least one `cir.await` instruction to be used in its body.
+
The `lambda` translates to a C++ `operator()` that implements a lambda, this
allow callsites to make certain assumptions about the real function nature
when writing analysis.
@@ -2347,11 +2353,22 @@ def CIR_FuncOp : CIR_Op<"func", [
// Linkage information
cir.func linkonce_odr @some_method(...)
```
+ // Builtin function
+ cir.func builtin @__builtin_coro_end(!cir.ptr<i8>, !cir.bool) -> !cir.bool
+ // Coroutine
+ cir.func coroutine @_Z10silly_taskv() -> !CoroTask {
+ ...
+ cir.await(...)
+ ...
+ }
+ ```
}];
let arguments = (ins SymbolNameAttr:$sym_name,
CIR_VisibilityAttr:$global_visibility,
TypeAttrOf<CIR_FuncType>:$function_type,
+ UnitAttr:$builtin,
+ UnitAttr:$coroutine,
UnitAttr:$lambda,
UnitAttr:$no_proto,
UnitAttr:$dso_local,
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 0fac1b211239a..73beeacce5de2 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -135,6 +135,13 @@ struct MissingFeatures {
static bool recordZeroInitPadding() { return false; }
static bool zeroSizeRecordMembers() { return false; }
+ // Coroutines
+ static bool coroAllocBuiltinCall() { return false; }
+ static bool coroBeginBuiltinCall() { return false; }
+ static bool coroEndBuiltinCall() { return false; }
+ static bool coroSizeBuiltinCall() { return false; }
+ static bool coroutineFrame() { return false; }
+
// Various handling of deferred processing in CIRGenModule.
static bool cgmRelease() { return false; }
static bool deferredVtables() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index cf17de144f4d9..512e1b2aa0e86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -428,6 +428,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return emitUnaryFPBuiltin<cir::ATanOp>(*this, *e);
case Builtin::BI__builtin_elementwise_cos:
return emitUnaryFPBuiltin<cir::CosOp>(*this, *e);
+ case Builtin::BI__builtin_coro_id:
+ case Builtin::BI__builtin_coro_promise:
+ case Builtin::BI__builtin_coro_resume:
+ case Builtin::BI__builtin_coro_noop:
+ case Builtin::BI__builtin_coro_destroy:
+ case Builtin::BI__builtin_coro_done:
+ case Builtin::BI__builtin_coro_alloc:
+ case Builtin::BI__builtin_coro_begin:
+ case Builtin::BI__builtin_coro_end:
+ case Builtin::BI__builtin_coro_suspend:
+ case Builtin::BI__builtin_coro_align:
+ llvm_unreachable("BI__builtin_coro_id like NYI");
+
+ case Builtin::BI__builtin_coro_frame: {
+ cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI");
+ assert(!cir::MissingFeatures::coroutineFrame());
+ return getUndefRValue(e->getType());
+ }
+ case Builtin::BI__builtin_coro_free:
+ case Builtin::BI__builtin_coro_size: {
+ cgm.errorNYI(e->getSourceRange(),
+ "BI__builtin_coro_free, BI__builtin_coro_size NYI");
+ assert(!cir::MissingFeatures::coroSizeBuiltinCall());
+ return getUndefRValue(e->getType());
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
new file mode 100644
index 0000000000000..ee43c221c3cd6
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -0,0 +1,85 @@
+//===----- CGCoroutine.cpp - Emit CIR Code for C++ coroutines -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code dealing with C++ code generation of coroutines.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+#include "mlir/Support/LLVM.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+struct clang::CIRGen::CGCoroData {
+ // Stores the __builtin_coro_id emitted in the function so that we can supply
+ // it as the first argument to other builtins.
+ cir::CallOp coroId = nullptr;
+};
+
+// Defining these here allows to keep CGCoroData private to this file.
+CIRGenFunction::CGCoroInfo::CGCoroInfo() {}
+CIRGenFunction::CGCoroInfo::~CGCoroInfo() {}
+
+static void createCoroData(CIRGenFunction &cgf,
+ CIRGenFunction::CGCoroInfo &curCoro,
+ cir::CallOp coroId) {
+ if (curCoro.data) {
+ llvm_unreachable("EmitCoroutineBodyStatement called twice?");
+ return;
+ }
+
+ curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData);
+ curCoro.data->coroId = coroId;
+}
+
+cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
+ mlir::Value nullPtr) {
+ cir::IntType int32Ty = builder.getUInt32Ty();
+
+ const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
+ unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
+
+ mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroId);
+
+ cir::FuncOp fnOp;
+ if (!builtin) {
+ fnOp = cgm.createCIRBuiltinFunction(
+ loc, cgm.builtinCoroId,
+ cir::FuncType::get({int32Ty, VoidPtrTy, VoidPtrTy, VoidPtrTy}, int32Ty),
+ /*FD=*/nullptr);
+ assert(fnOp && "should always succeed");
+ } else
+ fnOp = cast<cir::FuncOp>(builtin);
+
+ return builder.createCallOp(loc, fnOp,
+ mlir::ValueRange{builder.getUInt32(newAlign, loc),
+ nullPtr, nullPtr, nullPtr});
+}
+
+mlir::LogicalResult
+CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
+ mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
+ cir::ConstantOp nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc);
+
+ cir::FuncOp fn = curFn;
+ assert(fn && "other callables are NYI");
+ fn.setCoroutine(true);
+ cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
+ createCoroData(*this, curCoro, coroId);
+
+ assert(!cir::MissingFeatures::coroAllocBuiltinCall());
+
+ assert(!cir::MissingFeatures::coroBeginBuiltinCall());
+
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ return mlir::success();
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0abb21a670719..f435979801af9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -342,6 +342,9 @@ void CIRGenFunction::LexicalScope::cleanup() {
cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
CIRGenBuilderTy &builder = cgf.getBuilder();
+ // If we are on a coroutine, add the coro_end builtin call.
+ assert(!cir::MissingFeatures::coroEndBuiltinCall());
+
if (!cgf.curFn.getFunctionType().hasVoidReturn()) {
// Load the value from `__retval` and return it via the `cir.return` op.
auto value = builder.create<cir::LoadOp>(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 166435f9e7e9e..ae8d14b1e98e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -47,6 +47,8 @@ class LoopOp;
namespace clang::CIRGen {
+struct CGCoroData;
+
class CIRGenFunction : public CIRGenTypeCache {
public:
CIRGenModule &cgm;
@@ -66,6 +68,18 @@ class CIRGenFunction : public CIRGenTypeCache {
/// The compiler-generated variable that holds the return value.
std::optional<mlir::Value> fnRetAlloca;
+ // Holds coroutine data if the current function is a coroutine. We use a
+ // wrapper to manage its lifetime, so that we don't have to define CGCoroData
+ // in this header.
+ struct CGCoroInfo {
+ std::unique_ptr<CGCoroData> data;
+ CGCoroInfo();
+ ~CGCoroInfo();
+ };
+ CGCoroInfo curCoro;
+
+ bool isCoroutine() const { return curCoro.data != nullptr; }
+
/// The temporary alloca to hold the return value. This is
/// invalid iff the function has no return value.
Address returnValue = Address::invalid();
@@ -1168,6 +1182,10 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitConstructorBody(FunctionArgList &args);
+ mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s);
+ cir::CallOp emitCoroEndBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
+ cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
+
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);
void emitDestructorBody(FunctionArgList &args);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index eef23a0ebda7f..e36df8da1d6c0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2043,6 +2043,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
return func;
}
+cir::FuncOp
+CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
+ cir::FuncType ty,
+ const clang::FunctionDecl *fd) {
+ cir::FuncOp fnOp = createCIRFunction(loc, name, ty, fd);
+ fnOp.setBuiltin(true);
+ return fnOp;
+}
+
mlir::SymbolTable::Visibility
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
// MLIR doesn't accept public symbols declarations (only
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 073e8d96b773b..6cf40874e0997 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -448,6 +448,13 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType funcType,
const clang::FunctionDecl *funcDecl);
+ /// Create a CIR function with builtin attribute set.
+ cir::FuncOp createCIRBuiltinFunction(mlir::Location loc, llvm::StringRef name,
+ cir::FuncType ty,
+ const clang::FunctionDecl *fd);
+
+ static constexpr const char *builtinCoroId = "__builtin_coro_id";
+
/// Given a builtin id for a function like "__builtin_fabsf", return a
/// Function* for "fabsf".
cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID);
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index e842892d085d2..22a1c54015e63 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -197,6 +197,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::SEHLeaveStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::CoroutineBodyStmtClass:
+ return emitCoroutineBody(cast<CoroutineBodyStmt>(*s));
case Stmt::CoreturnStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::IndirectGotoStmtClass:
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index c1f27ec8ba858..73fddb184a792 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -14,6 +14,7 @@ add_clang_library(clangCIR
CIRGenCall.cpp
CIRGenClass.cpp
CIRGenCleanup.cpp
+ CIRGenCoroutine.cpp
CIRGenCXX.cpp
CIRGenCXXABI.cpp
CIRGenBuiltin.cpp
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 58ef500446aa7..dae9162214e6c 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1546,12 +1546,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
llvm::SMLoc loc = parser.getCurrentLocation();
mlir::Builder &builder = parser.getBuilder();
+ mlir::StringAttr builtinNameAttr = getBuiltinAttrName(state.name);
+ mlir::StringAttr coroutineNameAttr = getCoroutineAttrName(state.name);
mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name);
mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
+ if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
+ state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
+ if (::mlir::succeeded(
+ parser.parseOptionalKeyword(coroutineNameAttr.strref())))
+ state.addAttribute(coroutineNameAttr, parser.getBuilder().getUnitAttr());
if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref())))
state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr());
if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
@@ -1661,6 +1668,12 @@ mlir::Region *cir::FuncOp::getCallableRegion() {
}
void cir::FuncOp::print(OpAsmPrinter &p) {
+ if (getBuiltin())
+ p << " builtin";
+
+ if (getCoroutine())
+ p << " coroutine";
+
if (getLambda())
p << " lambda";
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
new file mode 100644
index 0000000000000..1fc7d77be2bce
--- /dev/null
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+
+namespace std {
+
+template<typename T> struct remove_reference { typedef T type; };
+template<typename T> struct remove_reference<T &> { typedef T type; };
+template<typename T> struct remove_reference<T &&> { typedef T type; };
+
+template<typename T>
+typename remove_reference<T>::type &&move(T &&t) noexcept;
+
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <>
+struct coroutine_handle<void> {
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+ static coroutine_handle from_address(void *);
+};
+
+struct suspend_always {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+} // namespace std
+
+namespace folly {
+namespace coro {
+
+using std::suspend_always;
+using std::suspend_never;
+using std::coroutine_handle;
+
+using SemiFuture = int;
+
+template<class T>
+struct Task {
+ struct promise_type {
+ Task<T> get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_value(T);
+ void unhandled_exception();
+ auto yield_value(Task<T>) noexcept { return final_suspend(); }
+ };
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ T await_resume();
+};
+
+template<>
+struct Task<void> {
+ struct promise_type {
+ Task<void> get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ void unhandled_exception() noexcept;
+ auto yield_value(Task<void>) noexcept { return final_suspend(); }
+ };
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+ SemiFuture semi();
+};
+
+// FIXME: add CIRGen support here.
+// struct blocking_wait_fn {
+// template <typename T>
+// T operator()(Task<T>&& awaitable) const {
+// return T();
+// }
+// };
+
+// inline constexpr blocking_wait_fn blocking_wait{};
+// static constexpr blocking_wait_fn const& blockingWait = blocking_wait;
+
+struct co_invoke_fn {
+ template <typename F, typename... A>
+ Task<void> operator()(F&& f, A&&... a) const {
+ return Task<void>();
+ }
+};
+
+co_invoke_fn co_invoke;
+
+}} // namespace folly::coro
+
+// CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" padded {!u8i}>
+
+// CIR: module {{.*}} {
+// CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn
+
+// CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i
+
+using VoidTask = folly::coro::Task<void>;
+
+VoidTask silly_task() {
+ co_await std::suspend_always();
+}
+
+// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]
+// CHECK: %[[#VoidTaskAddr:]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
+
+// Get coroutine id with __builtin_coro_id.
+
+// CIR: %[[NullPtr:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i
+// CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]])
|
@llvm/pr-subscribers-clangir Author: None (Andres-Salamanca) ChangesThis PR adds new Full diff: https://github.com/llvm/llvm-project/pull/161616.diff 12 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bb394440bf8d8..c19b90d30bf94 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2326,6 +2326,12 @@ def CIR_FuncOp : CIR_Op<"func", [
The function linkage information is specified by `linkage`, as defined by
`GlobalLinkageKind` attribute.
+ A compiler builtin function must be marked as `builtin` for further
+ processing when lowering from CIR.
+
+ The `coroutine` keyword is used to mark coroutine function, which requires
+ at least one `cir.await` instruction to be used in its body.
+
The `lambda` translates to a C++ `operator()` that implements a lambda, this
allow callsites to make certain assumptions about the real function nature
when writing analysis.
@@ -2347,11 +2353,22 @@ def CIR_FuncOp : CIR_Op<"func", [
// Linkage information
cir.func linkonce_odr @some_method(...)
```
+ // Builtin function
+ cir.func builtin @__builtin_coro_end(!cir.ptr<i8>, !cir.bool) -> !cir.bool
+ // Coroutine
+ cir.func coroutine @_Z10silly_taskv() -> !CoroTask {
+ ...
+ cir.await(...)
+ ...
+ }
+ ```
}];
let arguments = (ins SymbolNameAttr:$sym_name,
CIR_VisibilityAttr:$global_visibility,
TypeAttrOf<CIR_FuncType>:$function_type,
+ UnitAttr:$builtin,
+ UnitAttr:$coroutine,
UnitAttr:$lambda,
UnitAttr:$no_proto,
UnitAttr:$dso_local,
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 0fac1b211239a..73beeacce5de2 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -135,6 +135,13 @@ struct MissingFeatures {
static bool recordZeroInitPadding() { return false; }
static bool zeroSizeRecordMembers() { return false; }
+ // Coroutines
+ static bool coroAllocBuiltinCall() { return false; }
+ static bool coroBeginBuiltinCall() { return false; }
+ static bool coroEndBuiltinCall() { return false; }
+ static bool coroSizeBuiltinCall() { return false; }
+ static bool coroutineFrame() { return false; }
+
// Various handling of deferred processing in CIRGenModule.
static bool cgmRelease() { return false; }
static bool deferredVtables() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index cf17de144f4d9..512e1b2aa0e86 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -428,6 +428,31 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return emitUnaryFPBuiltin<cir::ATanOp>(*this, *e);
case Builtin::BI__builtin_elementwise_cos:
return emitUnaryFPBuiltin<cir::CosOp>(*this, *e);
+ case Builtin::BI__builtin_coro_id:
+ case Builtin::BI__builtin_coro_promise:
+ case Builtin::BI__builtin_coro_resume:
+ case Builtin::BI__builtin_coro_noop:
+ case Builtin::BI__builtin_coro_destroy:
+ case Builtin::BI__builtin_coro_done:
+ case Builtin::BI__builtin_coro_alloc:
+ case Builtin::BI__builtin_coro_begin:
+ case Builtin::BI__builtin_coro_end:
+ case Builtin::BI__builtin_coro_suspend:
+ case Builtin::BI__builtin_coro_align:
+ llvm_unreachable("BI__builtin_coro_id like NYI");
+
+ case Builtin::BI__builtin_coro_frame: {
+ cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI");
+ assert(!cir::MissingFeatures::coroutineFrame());
+ return getUndefRValue(e->getType());
+ }
+ case Builtin::BI__builtin_coro_free:
+ case Builtin::BI__builtin_coro_size: {
+ cgm.errorNYI(e->getSourceRange(),
+ "BI__builtin_coro_free, BI__builtin_coro_size NYI");
+ assert(!cir::MissingFeatures::coroSizeBuiltinCall());
+ return getUndefRValue(e->getType());
+ }
}
// If this is an alias for a lib function (e.g. __builtin_sin), emit
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
new file mode 100644
index 0000000000000..ee43c221c3cd6
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -0,0 +1,85 @@
+//===----- CGCoroutine.cpp - Emit CIR Code for C++ coroutines -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code dealing with C++ code generation of coroutines.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+#include "mlir/Support/LLVM.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+struct clang::CIRGen::CGCoroData {
+ // Stores the __builtin_coro_id emitted in the function so that we can supply
+ // it as the first argument to other builtins.
+ cir::CallOp coroId = nullptr;
+};
+
+// Defining these here allows to keep CGCoroData private to this file.
+CIRGenFunction::CGCoroInfo::CGCoroInfo() {}
+CIRGenFunction::CGCoroInfo::~CGCoroInfo() {}
+
+static void createCoroData(CIRGenFunction &cgf,
+ CIRGenFunction::CGCoroInfo &curCoro,
+ cir::CallOp coroId) {
+ if (curCoro.data) {
+ llvm_unreachable("EmitCoroutineBodyStatement called twice?");
+ return;
+ }
+
+ curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData);
+ curCoro.data->coroId = coroId;
+}
+
+cir::CallOp CIRGenFunction::emitCoroIDBuiltinCall(mlir::Location loc,
+ mlir::Value nullPtr) {
+ cir::IntType int32Ty = builder.getUInt32Ty();
+
+ const TargetInfo &ti = cgm.getASTContext().getTargetInfo();
+ unsigned newAlign = ti.getNewAlign() / ti.getCharWidth();
+
+ mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroId);
+
+ cir::FuncOp fnOp;
+ if (!builtin) {
+ fnOp = cgm.createCIRBuiltinFunction(
+ loc, cgm.builtinCoroId,
+ cir::FuncType::get({int32Ty, VoidPtrTy, VoidPtrTy, VoidPtrTy}, int32Ty),
+ /*FD=*/nullptr);
+ assert(fnOp && "should always succeed");
+ } else
+ fnOp = cast<cir::FuncOp>(builtin);
+
+ return builder.createCallOp(loc, fnOp,
+ mlir::ValueRange{builder.getUInt32(newAlign, loc),
+ nullPtr, nullPtr, nullPtr});
+}
+
+mlir::LogicalResult
+CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
+ mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
+ cir::ConstantOp nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc);
+
+ cir::FuncOp fn = curFn;
+ assert(fn && "other callables are NYI");
+ fn.setCoroutine(true);
+ cir::CallOp coroId = emitCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
+ createCoroData(*this, curCoro, coroId);
+
+ assert(!cir::MissingFeatures::coroAllocBuiltinCall());
+
+ assert(!cir::MissingFeatures::coroBeginBuiltinCall());
+
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ return mlir::success();
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0abb21a670719..f435979801af9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -342,6 +342,9 @@ void CIRGenFunction::LexicalScope::cleanup() {
cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
CIRGenBuilderTy &builder = cgf.getBuilder();
+ // If we are on a coroutine, add the coro_end builtin call.
+ assert(!cir::MissingFeatures::coroEndBuiltinCall());
+
if (!cgf.curFn.getFunctionType().hasVoidReturn()) {
// Load the value from `__retval` and return it via the `cir.return` op.
auto value = builder.create<cir::LoadOp>(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 166435f9e7e9e..ae8d14b1e98e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -47,6 +47,8 @@ class LoopOp;
namespace clang::CIRGen {
+struct CGCoroData;
+
class CIRGenFunction : public CIRGenTypeCache {
public:
CIRGenModule &cgm;
@@ -66,6 +68,18 @@ class CIRGenFunction : public CIRGenTypeCache {
/// The compiler-generated variable that holds the return value.
std::optional<mlir::Value> fnRetAlloca;
+ // Holds coroutine data if the current function is a coroutine. We use a
+ // wrapper to manage its lifetime, so that we don't have to define CGCoroData
+ // in this header.
+ struct CGCoroInfo {
+ std::unique_ptr<CGCoroData> data;
+ CGCoroInfo();
+ ~CGCoroInfo();
+ };
+ CGCoroInfo curCoro;
+
+ bool isCoroutine() const { return curCoro.data != nullptr; }
+
/// The temporary alloca to hold the return value. This is
/// invalid iff the function has no return value.
Address returnValue = Address::invalid();
@@ -1168,6 +1182,10 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitConstructorBody(FunctionArgList &args);
+ mlir::LogicalResult emitCoroutineBody(const CoroutineBodyStmt &s);
+ cir::CallOp emitCoroEndBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
+ cir::CallOp emitCoroIDBuiltinCall(mlir::Location loc, mlir::Value nullPtr);
+
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);
void emitDestructorBody(FunctionArgList &args);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index eef23a0ebda7f..e36df8da1d6c0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2043,6 +2043,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
return func;
}
+cir::FuncOp
+CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name,
+ cir::FuncType ty,
+ const clang::FunctionDecl *fd) {
+ cir::FuncOp fnOp = createCIRFunction(loc, name, ty, fd);
+ fnOp.setBuiltin(true);
+ return fnOp;
+}
+
mlir::SymbolTable::Visibility
CIRGenModule::getMLIRVisibility(cir::GlobalOp op) {
// MLIR doesn't accept public symbols declarations (only
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 073e8d96b773b..6cf40874e0997 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -448,6 +448,13 @@ class CIRGenModule : public CIRGenTypeCache {
cir::FuncType funcType,
const clang::FunctionDecl *funcDecl);
+ /// Create a CIR function with builtin attribute set.
+ cir::FuncOp createCIRBuiltinFunction(mlir::Location loc, llvm::StringRef name,
+ cir::FuncType ty,
+ const clang::FunctionDecl *fd);
+
+ static constexpr const char *builtinCoroId = "__builtin_coro_id";
+
/// Given a builtin id for a function like "__builtin_fabsf", return a
/// Function* for "fabsf".
cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID);
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index e842892d085d2..22a1c54015e63 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -197,6 +197,7 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::SEHLeaveStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::CoroutineBodyStmtClass:
+ return emitCoroutineBody(cast<CoroutineBodyStmt>(*s));
case Stmt::CoreturnStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::IndirectGotoStmtClass:
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index c1f27ec8ba858..73fddb184a792 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -14,6 +14,7 @@ add_clang_library(clangCIR
CIRGenCall.cpp
CIRGenClass.cpp
CIRGenCleanup.cpp
+ CIRGenCoroutine.cpp
CIRGenCXX.cpp
CIRGenCXXABI.cpp
CIRGenBuiltin.cpp
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 58ef500446aa7..dae9162214e6c 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1546,12 +1546,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
llvm::SMLoc loc = parser.getCurrentLocation();
mlir::Builder &builder = parser.getBuilder();
+ mlir::StringAttr builtinNameAttr = getBuiltinAttrName(state.name);
+ mlir::StringAttr coroutineNameAttr = getCoroutineAttrName(state.name);
mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name);
mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name);
mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
+ if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref())))
+ state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr());
+ if (::mlir::succeeded(
+ parser.parseOptionalKeyword(coroutineNameAttr.strref())))
+ state.addAttribute(coroutineNameAttr, parser.getBuilder().getUnitAttr());
if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref())))
state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr());
if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
@@ -1661,6 +1668,12 @@ mlir::Region *cir::FuncOp::getCallableRegion() {
}
void cir::FuncOp::print(OpAsmPrinter &p) {
+ if (getBuiltin())
+ p << " builtin";
+
+ if (getCoroutine())
+ p << " coroutine";
+
if (getLambda())
p << " lambda";
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
new file mode 100644
index 0000000000000..1fc7d77be2bce
--- /dev/null
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+
+namespace std {
+
+template<typename T> struct remove_reference { typedef T type; };
+template<typename T> struct remove_reference<T &> { typedef T type; };
+template<typename T> struct remove_reference<T &&> { typedef T type; };
+
+template<typename T>
+typename remove_reference<T>::type &&move(T &&t) noexcept;
+
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void>
+struct coroutine_handle {
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <>
+struct coroutine_handle<void> {
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+ static coroutine_handle from_address(void *);
+};
+
+struct suspend_always {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+struct suspend_never {
+ bool await_ready() noexcept { return true; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+};
+
+} // namespace std
+
+namespace folly {
+namespace coro {
+
+using std::suspend_always;
+using std::suspend_never;
+using std::coroutine_handle;
+
+using SemiFuture = int;
+
+template<class T>
+struct Task {
+ struct promise_type {
+ Task<T> get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_value(T);
+ void unhandled_exception();
+ auto yield_value(Task<T>) noexcept { return final_suspend(); }
+ };
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ T await_resume();
+};
+
+template<>
+struct Task<void> {
+ struct promise_type {
+ Task<void> get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ void unhandled_exception() noexcept;
+ auto yield_value(Task<void>) noexcept { return final_suspend(); }
+ };
+ bool await_ready() noexcept { return false; }
+ void await_suspend(coroutine_handle<>) noexcept {}
+ void await_resume() noexcept {}
+ SemiFuture semi();
+};
+
+// FIXME: add CIRGen support here.
+// struct blocking_wait_fn {
+// template <typename T>
+// T operator()(Task<T>&& awaitable) const {
+// return T();
+// }
+// };
+
+// inline constexpr blocking_wait_fn blocking_wait{};
+// static constexpr blocking_wait_fn const& blockingWait = blocking_wait;
+
+struct co_invoke_fn {
+ template <typename F, typename... A>
+ Task<void> operator()(F&& f, A&&... a) const {
+ return Task<void>();
+ }
+};
+
+co_invoke_fn co_invoke;
+
+}} // namespace folly::coro
+
+// CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" padded {!u8i}>
+
+// CIR: module {{.*}} {
+// CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn
+
+// CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i
+
+using VoidTask = folly::coro::Task<void>;
+
+VoidTask silly_task() {
+ co_await std::suspend_always();
+}
+
+// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]
+// CHECK: %[[#VoidTaskAddr:]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]
+
+// Get coroutine id with __builtin_coro_id.
+
+// CIR: %[[NullPtr:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i
+// CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]])
|
6637f98
to
860bfd2
Compare
A compiler builtin function must be marked as `builtin` for further | ||
processing when lowering from CIR. | ||
|
||
The `coroutine` keyword is used to mark coroutine function, which requires |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The `coroutine` keyword is used to mark coroutine function, which requires | |
The `coroutine` keyword is used to mark a coroutine function, which requires |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
case Builtin::BI__builtin_coro_end: | ||
case Builtin::BI__builtin_coro_suspend: | ||
case Builtin::BI__builtin_coro_align: | ||
llvm_unreachable("BI__builtin_coro_id like NYI"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
errorNYI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
if (curCoro.data) { | ||
llvm_unreachable("EmitCoroutineBodyStatement called twice?"); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (curCoro.data) { | |
llvm_unreachable("EmitCoroutineBodyStatement called twice?"); | |
return; | |
} | |
assert(!curCoro.data && "EmitCoroutineBodyStatement called twice?"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
return; | ||
} | ||
|
||
curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curCoro.data = std::unique_ptr<CGCoroData>(new CGCoroData); | |
curCoro.data = std::make_unique<CGCoroData>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
cir::FuncType::get({int32Ty, VoidPtrTy, VoidPtrTy, VoidPtrTy}, int32Ty), | ||
/*FD=*/nullptr); | ||
assert(fnOp && "should always succeed"); | ||
} else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The else
needs braces since the if
requires them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
auto fn = dyn_cast<cir::FuncOp>(curFn); | ||
assert(fn && "other callables are NYI"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto fn = dyn_cast<cir::FuncOp>(curFn); | |
assert(fn && "other callables are NYI"); | |
auto fn = mlir::cast<cir::FuncOp>(curFn); |
I don't think they're just NYI. I don't think it's allowed. The other thing this can be is a global variable for which we are generating a ctor region. Using cast
makes the assertion part of the cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Yes, before I rebased, curFn was a cir::FuncOp, so it was a quick change. Sorry about that.
mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name); | ||
mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); | ||
|
||
if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an IR test to verify the parsing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good apart from one nit
case Builtin::BI__builtin_coro_end: | ||
case Builtin::BI__builtin_coro_suspend: | ||
case Builtin::BI__builtin_coro_align: | ||
llvm_unreachable("Error NYI"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. I meant that this should be a call to cgm.errorNYI()
rather than llvm_unreachable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, sorry yes, that makes more sense. Done
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/154/builds/22303 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/72/builds/15342 Here is the relevant piece of the build log for the reference
|
This PR adds new
FuncOp
attributes (coroutine
andbuiltin
) and begins the implementation of theemitCoroutineBody
function. Feature markers were also added for guidance in future PRs.